package individual;

import java.util.Random;

import org.omg.PortableServer.THREAD_POLICY_ID;

import environment.Environment;

import representation.Memory;
import representation.Value;
import utils.Node;


public class Pindividual extends Thread {
	
	private static final boolean PINDIVIDUAL_RUNNING = true;
	private static final boolean PINDIVIDUAL_DONE = false;
	
	public Node<Pindividual> container;
	public Memory codeMemory;
	private Memory stack;
	private Memory callStack;
	private Memory heap;
	public boolean terminate = false;
	public boolean started = false;
	private int priority;
	private Pinstruction instruction;	
	private Registers registers;
	public double score = 0.0;
	private static Random rand = new Random();
	public int memoryCap = 0;
	
	private Object lock = new Object();
	private Object threadLock = new Object();
		
	public Pindividual(int priority, int memSize)
	{
		/** initialize */
		this.priority = priority;
		setPriority(priority);
		
		/** initialize registers **/
		registers = new Registers();
		
		/** initialize memory **/
		codeMemory = new Memory(memSize);
		registers.setSP(250);
		stack = codeMemory.ref(registers.getSP());
		callStack = codeMemory.ref(registers.getSP(),false);
		heap = codeMemory.ref(600);
		
		/** initialize instruction **/
		instruction = new Pinstruction();		
		
	}
	
	public void start()
	{		
		synchronized (threadLock) {
			if(!started)
			{
				started = true;
				super.start();
				
			}	
		}		
	}
	
	public void increasePriority()
	{
		synchronized (lock) {
			if(priority < Thread.MAX_PRIORITY-1)
			{
				priority ++;
				setPriority(priority);
			}	
		}
		
	}
	
	public void decreasePriority()
	{
		synchronized (lock) {
			if(priority > Thread.MIN_PRIORITY)
			{
				priority --;
				setPriority(priority);
			}		
		}
		
	}
	
	public void donatePriority(Pindividual target)
	{
		synchronized (lock) {
			synchronized (target.lock) {
				if(priority > Thread.MIN_PRIORITY && target.priority < Thread.MAX_PRIORITY)
				{
					decreasePriority();
					target.increasePriority();
				}
				
			}			
		}
	}
	
	private static class RegisterSet
	{
		private static int ZERO = 0;
		private static int ONE = 1;
		
		/** results **/
		private static int AR = 2;
		private static int BR = 3;
		
		/** arguments **/
		private static int AG = 4;
		private static int BG = 5;
		private static int CG = 6;
		private static int DG = 7;
		private static int EG = 8;
		
		/** temporary **/
		private static int AT = 9;
		private static int BT = 10;
		private static int CT = 11;
		private static int DT = 12;
		private static int ET = 13;
		private static int FT = 14;
		private static int GT = 15;
		private static int HT = 16;
		private static int IT = 17;		
		
		/** permanant **/
		private static int AP = 18;
		private static int BP = 19;
		private static int CP = 20;
		private static int DP = 21;
		private static int EP = 22;
		private static int FP = 23;
		private static int GP = 24;
		private static int HP = 25;
		private static int IP = 26;
		
		/** base value **/
		private static int BV = 27;
		
		private static int PC = 28;
		private static int SP = 29;
		private static int RA = 30;
		private static int I = 31;

	}
	
	private class Registers
	{
		private int[] register;
		
		private Registers()
		{
			/** initialize registers */
			register = new int[32];
			
			for(int i = 0; i < register.length; i++)
				register[i] = 0;
			
			register[RegisterSet.ONE] = 1;
		}
		
		/** registers */
		private int getZero() { return register[RegisterSet.ZERO]; }	
		private int getOne() { return register[RegisterSet.ONE]; }	

		private int getAR() { return register[RegisterSet.AR]; }
		private int getBR() { return register[RegisterSet.BR]; }
		private int getAG() { return register[RegisterSet.AG]; }
		private int getBG() { return register[RegisterSet.BG]; }
		private int getCG() { return register[RegisterSet.CG]; }
		private int getDG() { return register[RegisterSet.DG]; }
		private int getEG() { return register[RegisterSet.EG]; }

		private int getAT() { return register[RegisterSet.AT]; }
		private int getBT() { return register[RegisterSet.BT]; }
		private int getCT() { return register[RegisterSet.CT]; }
		private int getDT() { return register[RegisterSet.DT]; }
		private int getET() { return register[RegisterSet.ET]; }
		private int getFT() { return register[RegisterSet.FT]; }
		private int getGT() { return register[RegisterSet.GT]; }
		private int getHT() { return register[RegisterSet.HT]; }
		private int getIT() { return register[RegisterSet.IT]; }
		

		private int getAP() { return register[RegisterSet.AP]; }
		private int getBP() { return register[RegisterSet.BP]; }
		private int getCP() { return register[RegisterSet.CP]; }
		private int getDP() { return register[RegisterSet.DP]; }
		private int getEP() { return register[RegisterSet.EP]; }
		private int getFP() { return register[RegisterSet.FP]; }
		private int getGP() { return register[RegisterSet.GP]; }
		private int getHP() { return register[RegisterSet.HP]; }
		private int getIP() { return register[RegisterSet.IP]; }
		
		private int getBV() { return register[RegisterSet.BV]; }

		private int getPC() { return register[RegisterSet.PC]; }
		private int getSP() { return register[RegisterSet.SP]; }
		private int getRA() { return register[RegisterSet.RA]; }
		
		
		private int getInstruction() { return register[RegisterSet.I]; }

		/** equivalent setters */
		private void setAR(int value) {  register[RegisterSet.AR] = value; }
		private void setBR(int value) {  register[RegisterSet.BR] = value; }
		private void setAG(int value) {  register[RegisterSet.AG] = value; }
		private void setBG(int value) {  register[RegisterSet.BG] = value; }
		private void setCG(int value) {  register[RegisterSet.CG] = value; }
		private void setDG(int value) {  register[RegisterSet.DG] = value; }
		private void setEG(int value) {  register[RegisterSet.EG] = value; }

		private void setAT(int value) {  register[RegisterSet.AT] = value; }
		private void setBT(int value) {  register[RegisterSet.BT] = value; }
		private void setCT(int value) {  register[RegisterSet.CT] = value; }
		private void setDT(int value) {  register[RegisterSet.DT] = value; }
		private void setET(int value) {  register[RegisterSet.ET] = value; }
		private void setFT(int value) {  register[RegisterSet.FT] = value; }
		private void setGT(int value) {  register[RegisterSet.GT] = value; }
		private void setHT(int value) {  register[RegisterSet.HT] = value; }
		private void setIT(int value) {  register[RegisterSet.IT] = value; }
		

		private void setAP(int value) {  register[RegisterSet.AP] = value; }
		private void setBP(int value) {  register[RegisterSet.BP] = value; }
		private void setCP(int value) {  register[RegisterSet.CP] = value; }
		private void setDP(int value) {  register[RegisterSet.DP] = value; }
		private void setEP(int value) {  register[RegisterSet.EP] = value; }
		private void setFP(int value) {  register[RegisterSet.FP] = value; }
		private void setGP(int value) {  register[RegisterSet.GP] = value; }
		private void setHP(int value) {  register[RegisterSet.HP] = value; }
		private void setIP(int value) {  register[RegisterSet.IP] = value; }
		private void setBV(int value) {  register[RegisterSet.BV] = value; }

		private void setPC(int value) {  register[RegisterSet.PC] = value; }
		private void setSP(int value) {  register[RegisterSet.SP] = value; }
		private void setRA(int value) {  register[RegisterSet.RA] = value; }
	
		private void setInstruction(int value) { register[RegisterSet.I] = value; }
		
	}
	
	private static class PinstructionSet
	{	
		private static final int INSTPART = Integer.parseInt("11111",2);
		private static final int INSTSHIFT = 0;

		private static final int ISIMMPART = Integer.parseInt("100000",2);
		private static final int ISIMMSHIFT = 5;

		private static final int ARG0PART = Integer.parseInt("11111000000",2);
		private static final int ARG0SHIFT = 6;

		private static final int ARG1PART = Integer.parseInt("1111100000000000",2);
		private static final int ARG1SHIFT = 11;

		private static final int ARG2PART = Integer.parseInt("111110000000000000000",2);
		private static final int ARG2SHIFT = 16;

		private static final int ARG3PART = Integer.parseInt("11111000000000000000000000",2);
		private static final int ARG3SHIFT = 21;

		private static final int ARG4PART = Integer.parseInt("1111100000000000000000000000000",2);
		private static final int ARG4SHIFT = 26;
		 
		private static final int ARGIMMPART = Integer.parseInt("1111111111111111000000000000000", 2);
		private static final int ARGIMMSHIFT = 15;
		
		private static final int NOP = 0;
		private static final int LOAD = 1;		
		private static final int SAVE = 2;
		private static final int MOVE = 3;
		private static final int MOVEIMMHIGH = 4;
		private static final int MOVEIMMLOW = 5;
				
		private static final int COMP = 6;
		private static final int JMP = 7;
		private static final int JNZ = 8;
		private static final int JZ = 9;
		private static final int JLZ = 10;
		private static final int JGZ = 11;
		private static final int JLEZ = 12;
		private static final int JGEZ = 13;
		private static final int CALL = 14;
		private static final int RET = 15;
		private static final int JMPABS = 16;
		
		private static final int ADD = 17;
		private static final int SUB = 18;
		private static final int MUL = 19;
		private static final int DIV = 20;
		
		private static final int AND = 21;
		private static final int OR = 22;
		private static final int NOT = 23;
		private static final int XOR = 24;
		
		private static final int PUSH = 25;
		private static final int POP = 26;
		
		private static final int SYSCALL = 27;
		
		private static final int TRANSFER = 28;
		
		/** */
		private static final int RAND = 1; /** return a random number */
		private static final int START = 2; /** start a thread */
		private static final int SPLITSELF = 3; /** split oneself - by proportion*/
		private static final int CLAIM = 4; /** claim extra memory */
		private static final int RETURN = 5; /** return memory */
		private static final int LOCK = 6; /** lock save operation */
		private static final int MEMSIZE = 7; 
		private static final int UNLOCK = 8;
		private static final int DONATEPRIORITY = 9;
		
		private static int buildInstruction(int insname, int reg0, int reg1, int reg2)
		{
			int instruction = (insname << PinstructionSet.INSTSHIFT) & PinstructionSet.INSTPART;
			instruction = instruction | ((reg0 << PinstructionSet.ARG0SHIFT) & PinstructionSet.ARG0PART);
			instruction = instruction | ((reg1 << PinstructionSet.ARG1SHIFT) & PinstructionSet.ARG1PART);
			instruction = instruction | ((reg2 << PinstructionSet.ARG2SHIFT) & PinstructionSet.ARG2PART);
						
		
			return instruction;	
		}	
		
		private static int buildInstruction(int insname, int reg0, int reg1, int reg2, int reg3, int reg4)
		{
			int instruction = (insname << PinstructionSet.INSTSHIFT) & PinstructionSet.INSTPART;
			instruction = instruction | ((reg0 << PinstructionSet.ARG0SHIFT) & PinstructionSet.ARG0PART);
			instruction = instruction | ((reg1 << PinstructionSet.ARG1SHIFT) & PinstructionSet.ARG1PART);
			instruction = instruction | ((reg2 << PinstructionSet.ARG2SHIFT) & PinstructionSet.ARG2PART);
			instruction = instruction | ((reg3 << PinstructionSet.ARG3SHIFT) & PinstructionSet.ARG3PART);
			instruction = instruction | ((reg4 << PinstructionSet.ARG4SHIFT) & PinstructionSet.ARG4PART);
						
			return instruction;
		}
		
		private static int buildInstruction(int insname, int reg0, int imm)
		{
			int instruction = (insname << PinstructionSet.INSTSHIFT) & PinstructionSet.INSTPART;
			instruction = instruction | ((reg0 << PinstructionSet.ARG0SHIFT) & PinstructionSet.ARG0PART);			
			instruction = instruction | ((imm << PinstructionSet.ARGIMMSHIFT) & PinstructionSet.ARGIMMPART);
		
			return instruction;	
		}		
		private static int buildInstruction(int insname)
		{
			int instruction = (insname << PinstructionSet.INSTSHIFT) & PinstructionSet.INSTPART;
			return instruction;
		}
		
		private static int buildInstruction(int insname, int reg0)
		{
			int instruction = (insname << PinstructionSet.INSTSHIFT) & PinstructionSet.INSTPART;
			instruction = instruction | ((reg0 << PinstructionSet.ARG0SHIFT) & PinstructionSet.ARG0PART);
			return instruction;
		}
		
		private static int buildInstructionImm(int insname, int imm)
		{
			int instruction = (insname << PinstructionSet.INSTSHIFT) & PinstructionSet.INSTPART;
			instruction = instruction | ((1 << PinstructionSet.ISIMMSHIFT) & PinstructionSet.ISIMMPART);
			instruction = instruction | ((imm << PinstructionSet.ARGIMMSHIFT) & PinstructionSet.ARGIMMPART);
			
			return instruction;
		}
		
	
	}
	
	
	private class Pinstruction
	{	
		/** $0 <= [$1] */
		private void load(int[] regnames) {
			setReg( regnames[0], read( getReg(regnames[1])) ); 
		}		
		/** [$0] <= $1 */
		private void save(int[] regnames) {
			write( getReg(regnames[0]), getReg( regnames[1]) );
		}
		/** $0 <= $1 */
		private void move(int[] regnames) {
			setReg( regnames[0], getReg(regnames[1]) );
		}
		/** $0 <= imm */
		private void moveImmHigh(int[] regnames, int imm)
		{
			setRegHigh( regnames[0], imm );
		}
		
		private void moveImmLow(int[] regnames, int imm) 
		{
			setRegLow( regnames[0], imm );
		}
		/** $0 <== $1 == $2 */
		private void comp(int[] regnames) 
		{
			int value = getReg(regnames[1]) - getReg(regnames[2]);			
			setReg( regnames[0], value > 0 ? 1 : (value == 0 ? 0 : -1) );
		}
		
		/** PC <= PC + $0 **/
		private void jump(int[] regnames)
		{
			registers.setPC( registers.getPC() + getReg(regnames[0]) );
		}
		
		private void jump(int imm)
		{
			registers.setPC( (getHigh(registers.getBV()) | getLow(imm)) + registers.getPC() );
		}
		
		private void jumpAbsolute(int[] regnames)
		{
			registers.setPC( getReg(regnames[0]) );
		}
		
		private void jumpAbsolute(int imm)
		{	
			registers.setPC( getHigh(registers.getBV()) | getLow(imm) );
		}
		
		/** PC <= PC + $0 if $1 */
		private void jumpIfNotZero(int[] regnames)
		{
			if(getReg(regnames[1]) != 0)
				jump(regnames);			
		}
		
		private void jumpIfZero(int[] regnames)
		{
			if(getReg(regnames[1]) == 0)
				jump(regnames);			
		}
		
		private void jumpIfLessThanZero(int[] regnames)
		{
			if(getReg(regnames[1]) < 0)
				jump(regnames);			
		}
		
		private void jumpIfLessThanOrEqualToZero(int[] regnames)
		{
			if(getReg(regnames[1]) <= 0)
				jump(regnames);			
		}
		
		private void jumpIfGreaterThanZero(int[] regnames)
		{
			if(getReg(regnames[1]) > 0)
				jump(regnames);			
		}
		
		private void jumpIfGreaterThanOrEqualToZero(int[] regnames)
		{
			if(getReg(regnames[1]) >= 0)
				jump(regnames);			
		}
				
		/** $0 <= $1 + $2 */
		private void add(int[] regnames)
		{
			setReg(regnames[0], getReg(regnames[1]) + getReg(regnames[2]) ); 
		}
		
		private void sub(int[] regnames)
		{
			setReg(regnames[0], getReg(regnames[1]) - getReg(regnames[2]) ); 
		}
		
		private void mul(int[] regnames)
		{
			setReg(regnames[0], getReg(regnames[1]) + getReg(regnames[2]) ); 
		}
		
		private void div(int[] regnames)
		{
			int divisor = getReg(regnames[2]);
			setReg(regnames[0], getReg(regnames[1]) /(divisor != 0 ? divisor : 1) ); 
		}
		
		private void and(int[] regnames)
		{
			setReg(regnames[0], getReg(regnames[1]) & getReg(regnames[2]) ); 
		}
		
		private void or(int[] regnames)
		{
			setReg(regnames[0], getReg(regnames[1]) + getReg(regnames[2]) ); 
		}
		/** !$0 */
		private void not(int[] regnames)
		{
			setReg(regnames[0], getReg(regnames[1]) ^ 0xffffffff ); 
		}
		
		private void xor(int[] regnames)
		{
			setReg(regnames[0], getReg(regnames[1]) ^ getReg(regnames[2]) ); 
		}
				
		private void push(int[] regnames)
		{		
			setReg(RegisterSet.SP, getReg(RegisterSet.SP) - 1);
			write( getReg(RegisterSet.SP), getReg( regnames[0]) );
		}
		
		private void pop(int[] regnames)
		{			
			setReg(regnames[0], read( getReg(RegisterSet.SP) ));
			setReg(RegisterSet.SP, getReg(RegisterSet.SP) + 1);
			
		}
		
		private void call(int[] regnames)
		{	
			registers.setSP(getReg(RegisterSet.SP)-1); // sp--;
			write( registers.getSP(), registers.getPC() ); // mem[sp] = pc;	
			jumpAbsolute(regnames);
		}
		
		private void call(int imm)
		{
			registers.setSP(registers.getSP()-1); // sp--;	
			write( registers.getSP(), registers.getPC() );  // mem[sp] = pc;
			jumpAbsolute(imm);
		}
		
		private void ret(int[] regnames)
		{			
			registers.setPC( read( registers.getSP() ) ); // pc = mem[sp];			
			registers.setSP(registers.getSP()+1);	// sp++		
		}
		
		private void transfer(int[] regnames)
		{
			int sourcePos = getReg(regnames[0]);			
			int destPos = getReg(regnames[1]);
			int amount = getReg(regnames[2]);
			int sourceOffset = getReg(regnames[3]);
			int destOffset = getReg(regnames[4]);
			
			Pindividual sNeighbor = Environment.getNeighbor(Pindividual.this, sourcePos);
			Pindividual dNeighbor = Environment.getNeighbor(Pindividual.this, destPos);
			if(sNeighbor == null || dNeighbor == null)
				return;
			
			Memory source = sNeighbor.codeMemory.ref(sourceOffset);
			Memory dest = dNeighbor.codeMemory.ref(destOffset);
			
			source.copyTo(dest, amount);
			
		}

		private void syscall(int[] regNames)
		{
			int callnum = getReg(regNames[0]);
			int targetPos = 0;
			int amount = 0;

			switch(callnum)
			{
				case PinstructionSet.RAND:
					setReg(regNames[1], rand.nextInt());
					break;
				case PinstructionSet.SPLITSELF:
					/** create a neighbor pindividual, and donate some of my priority and memory */
					int proportion = getReg(regNames[1]);
					int size = codeMemory.getSize();
					//int center = size/2;
					//int cutpoint = (center + proportion) % size;// [][][][][][][] | [][][][][][][]
										
					decreasePriority();
					
					//codeMemory.diminish(size-cutpoint);
					//Pindividual baby = Pindividual.createEmpty(priority, size-cutpoint);
					codeMemory.diminish((int)(size/10.0));
					Pindividual baby = Pindividual.createEmpty(priority,(int)(9*size/10.0));
					//Pindividual baby = Pindividual.createEmpty(priority, size);
					Environment.addPindividual(baby, Pindividual.this);					
										
					break;				
				case PinstructionSet.START:
					/** start the neighboring pindividual: has no effect if already running */
					targetPos = getReg(regNames[1]);
					Pindividual neighbor = Environment.getNeighbor(Pindividual.this, targetPos);	
					if(neighbor != null)
						neighbor.start();					
					break;
				case PinstructionSet.LOCK:
					codeMemory.lock();
					/** lock save toward pindividual */
				case PinstructionSet.UNLOCK:
					codeMemory.unlock();
					break;				
				case PinstructionSet.CLAIM: /** not implemented */
					amount = getReg(regNames[1]);
					codeMemory.expand(amount);
					break;
				case PinstructionSet.RETURN: /** not implemented */					
					amount = getReg(regNames[1]);
					codeMemory.diminish(amount);
					break;
				case PinstructionSet.MEMSIZE:
					setReg(regNames[1], codeMemory.getSize());
					break;
				case PinstructionSet.DONATEPRIORITY:
					targetPos = getReg(regNames[1]);
					Pindividual target = Environment.getNeighbor(Pindividual.this, targetPos);
					if(target != null)
						donatePriority(target);					
					break;
				default:
					/*donothing*/
			}

		}
		
		private int read(int address)
		{
			return codeMemory.ref(address).getValue();
		}
		
		private void write(int address, int value)
		{
			codeMemory.ref(address).setValue(value);
		}
				 
		
		private void fetch() 
		{ 			
			int pc = registers.getPC();
			/** fetch instruction **/
			registers.setInstruction( read( pc ) );
			/** increment program counter **/
			registers.setPC( pc+1 );			
		}
		
		private void setReg(int index, int value) {
			registers.register[ index ] = value;
		}
		
		private int getReg(int index) {
			return registers.register[ index ];
		}
		
		private void setRegHigh(int index, int value) {
			registers.register[ index ] = (registers.register[ index ] & 0xffff) | (value & 0xffff0000); 
		}
		
		private void setRegLow(int index, int value) {
			registers.register[ index ] = (registers.register[ index ] & 0xffff0000) | (value & 0xffff);
		}

		
		
		private int execute()
		{
			int instruction = registers.getInstruction();
			int[] regnames = new int[5];			
			regnames[0] = (instruction & PinstructionSet.ARG0PART) >> PinstructionSet.ARG0SHIFT;
			regnames[1] = (instruction & PinstructionSet.ARG1PART) >> PinstructionSet.ARG1SHIFT;
			regnames[2] = (instruction & PinstructionSet.ARG2PART) >> PinstructionSet.ARG2SHIFT;
			regnames[3] = (instruction & PinstructionSet.ARG3PART) >> PinstructionSet.ARG3SHIFT;
			regnames[4] = (instruction & PinstructionSet.ARG4PART) >> PinstructionSet.ARG4SHIFT;
			int imm = (instruction & PinstructionSet.ARGIMMPART) >> PinstructionSet.ARGIMMSHIFT;
			int insname = (instruction & PinstructionSet.INSTPART) >> PinstructionSet.INSTSHIFT;
			boolean isImm = ((instruction & PinstructionSet.ISIMMPART) >> PinstructionSet.ISIMMSHIFT) == 1 ? true : false;
			
			switch( insname )
			{				
				case PinstructionSet.LOAD:
					load(regnames);
					break;
				case PinstructionSet.SAVE:
					save(regnames);
					break;
				case PinstructionSet.MOVE:
					move(regnames);
					break;
				case PinstructionSet.MOVEIMMHIGH:
					moveImmHigh(regnames, imm);
					break;
				case PinstructionSet.MOVEIMMLOW:
					moveImmLow(regnames, imm);
					break;
				case PinstructionSet.COMP:
					comp(regnames);
					break;
				case PinstructionSet.JMP:
					if(isImm)
						jump(imm);
					else
						jump(regnames);
					break;
				case PinstructionSet.JMPABS:
					if(isImm)
						jumpAbsolute(imm);
					else
						jumpAbsolute(regnames);
					break;				
				case PinstructionSet.JNZ:
					jumpIfNotZero(regnames);
					break;
				case PinstructionSet.JZ:
					jumpIfZero(regnames);
					break;
				case PinstructionSet.JLZ:
					jumpIfLessThanZero(regnames);
					break;
				case PinstructionSet.JGZ:
					jumpIfGreaterThanZero(regnames);
					break;
				case PinstructionSet.JLEZ:
					jumpIfLessThanOrEqualToZero(regnames);
					break;
				case PinstructionSet.JGEZ:
					jumpIfGreaterThanOrEqualToZero(regnames);
					break;
				case PinstructionSet.CALL:
					if(isImm)
						call(imm);
					else
						call(regnames);
					break;
				case PinstructionSet.RET:
					ret(regnames);
					break;				
				case PinstructionSet.ADD:
					add(regnames);
					break;
				case PinstructionSet.SUB:
					sub(regnames);
					break;
				case PinstructionSet.MUL:
					mul(regnames);
					break;
				case PinstructionSet.DIV:
					div(regnames);
					break;				
				case PinstructionSet.AND:
					and(regnames);
					break;
				case PinstructionSet.OR:
					or(regnames);
					break;
				case PinstructionSet.NOT:
					not(regnames);
					break;
				case PinstructionSet.XOR:
					xor(regnames);
					break;				
				case PinstructionSet.PUSH:
					push(regnames);
					break;
				case PinstructionSet.POP:
					pop(regnames);
					break;
				case PinstructionSet.SYSCALL:
					syscall(regnames);
					break;
				case PinstructionSet.TRANSFER:
					transfer(regnames);
					break;				
				default:
					/** do nothing : yield penalty**/
					yield();				
			}
			
			return insname;
						
		}		
		
	}
	
	private static int getLow(int value)
	{
		int low = 0;
		return low | (value & 0xffff);
	}
	
	private static int getHigh(int value)
	{
		int high = 0;
		return high | (value & 0xffff0000);
	}
	
	private boolean runInstruction()
	{
		/** make sure it's safe */		
		
		instruction.fetch();
		instruction.execute();
		
		return PINDIVIDUAL_RUNNING;
	}
	
	/** starting the thread **/
	public void run()
	{
		while(runInstruction() != PINDIVIDUAL_DONE && !this.terminate && (registers.getPC() < codeMemory.getSize()));	
		this.terminate = true;
	}
	

	public static Pindividual createEmpty(int priority, int memSize)
	{
		/** create a default individual? */
		Pindividual newGuy =  new Pindividual(priority, memSize);	
		
		int pc = 0;
		while(pc < memSize)
		{			
			newGuy.instruction.write(pc, rand.nextInt());
			pc++;
		}

		return newGuy;
	}
	
	static int instructions1[] = {						
			
		/** _init */
		PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMLOW, RegisterSet.AG, getLow(100)),
		PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMHIGH, RegisterSet.AG, getHigh(100)),
		PinstructionSet.buildInstruction(PinstructionSet.JMPABS, RegisterSet.AG),

		/** syscall split **/			
		PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMLOW, RegisterSet.AG, getLow(PinstructionSet.SPLITSELF)),
		PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMHIGH, RegisterSet.AG, getHigh(PinstructionSet.SPLITSELF)),
		PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.BG, RegisterSet.ZERO, RegisterSet.ZERO),
		PinstructionSet.buildInstruction(PinstructionSet.SYSCALL, RegisterSet.AG, RegisterSet.BG),
		PinstructionSet.buildInstruction(PinstructionSet.RET),
		
		/** transfer self **/
		PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.AG, RegisterSet.ZERO, RegisterSet.ZERO),
		PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.BG, RegisterSet.ZERO, RegisterSet.ZERO),
		PinstructionSet.buildInstruction(PinstructionSet.SUB, RegisterSet.BG, RegisterSet.BG, RegisterSet.ONE),
		PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.CG, RegisterSet.ZERO, RegisterSet.ZERO),
		PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.DG, RegisterSet.ZERO, RegisterSet.ZERO),			
		PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMLOW, RegisterSet.EG, getLow(0xfffffff)),
		PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMHIGH, RegisterSet.EG, getHigh(0xfffffff)),
		PinstructionSet.buildInstruction(PinstructionSet.TRANSFER, RegisterSet.AG, RegisterSet.BG, RegisterSet.CG, RegisterSet.DG, RegisterSet.EG),
		PinstructionSet.buildInstruction(PinstructionSet.RET),
		
		/** syscall activate **/
		PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMLOW, RegisterSet.AG, getLow(PinstructionSet.START)),
		PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMHIGH, RegisterSet.AG, getHigh(PinstructionSet.START)),
		PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.BG, RegisterSet.ZERO, RegisterSet.ZERO),
		PinstructionSet.buildInstruction(PinstructionSet.SUB, RegisterSet.BG, RegisterSet.BG, RegisterSet.ONE),
		PinstructionSet.buildInstruction(PinstructionSet.SYSCALL, RegisterSet.AG, RegisterSet.BG, RegisterSet.CG, RegisterSet.DG, RegisterSet.EG),
		PinstructionSet.buildInstruction(PinstructionSet.RET)
		
	};
	
	static int instructions2[] = {
			/** main */			
		PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMHIGH, RegisterSet.BV, getHigh(0)),			

		PinstructionSet.buildInstructionImm(PinstructionSet.CALL, getLow(3)), //split
		PinstructionSet.buildInstructionImm(PinstructionSet.CALL, getLow(8)), //transfer self (amount)			
		PinstructionSet.buildInstructionImm(PinstructionSet.CALL, getLow(17)), //activate		
		
		PinstructionSet.buildInstructionImm(PinstructionSet.JMPABS, getLow(0))

	};
	
	public static Pindividual createNatural(int priority, int memSize)
	{
		/** create a default individual? */
		Pindividual newGuy =  new Pindividual(Thread.NORM_PRIORITY, memSize);
		
		/** set default individual **/
		boolean done = false;
		
		
		int pc = 0;
				
		while(pc < instructions1.length)
		{			
			newGuy.instruction.write(pc, instructions1[pc]);
			pc++;
		}
		
		pc = 100;
		while(pc < 100 + instructions2.length)
		{			
			newGuy.instruction.write(pc, instructions2[pc-100]);
			pc++;
		}
		
		return newGuy;
	} 
	
	/** male **/
	public static Pindividual createNaturalM(int priority, int memSize)
	{
		/** create a default individual? */
		Pindividual newGuy =  new Pindividual(Thread.NORM_PRIORITY, memSize);
		
		/** set default individual **/
		boolean done = false;
		
		int instructionsm1[] = {						
			
			/** _init */
			PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMLOW, RegisterSet.AG, getLow(100)),
			PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMHIGH, RegisterSet.AG, getHigh(100)),
			PinstructionSet.buildInstruction(PinstructionSet.JMPABS, RegisterSet.AG),

			/** syscall split **/			
			PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMLOW, RegisterSet.AG, getLow(PinstructionSet.SPLITSELF)),
			PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMHIGH, RegisterSet.AG, getHigh(PinstructionSet.SPLITSELF)),
			PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.BG, RegisterSet.ZERO, RegisterSet.ZERO),
			PinstructionSet.buildInstruction(PinstructionSet.SYSCALL, RegisterSet.AG, RegisterSet.BG),
			PinstructionSet.buildInstruction(PinstructionSet.RET),
			
			/** transfer self **/
			PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.AG, RegisterSet.ZERO, RegisterSet.ZERO),
			PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.BG, RegisterSet.ZERO, RegisterSet.ZERO),
			PinstructionSet.buildInstruction(PinstructionSet.SUB, RegisterSet.BG, RegisterSet.BG, RegisterSet.ONE),
			PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.CG, RegisterSet.ZERO, RegisterSet.ZERO),
			PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.DG, RegisterSet.ZERO, RegisterSet.ZERO),			
			PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMLOW, RegisterSet.EG, getLow(0xfffffff)),
			PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMHIGH, RegisterSet.EG, getHigh(0xfffffff)),
			PinstructionSet.buildInstruction(PinstructionSet.TRANSFER, RegisterSet.AG, RegisterSet.BG, RegisterSet.CG, RegisterSet.DG, RegisterSet.EG),
			PinstructionSet.buildInstruction(PinstructionSet.RET),
			
			/** syscall activate **/
			PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMLOW, RegisterSet.AG, getLow(PinstructionSet.START)),
			PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMHIGH, RegisterSet.AG, getHigh(PinstructionSet.START)),
			PinstructionSet.buildInstruction(PinstructionSet.AND, RegisterSet.BG, RegisterSet.ZERO, RegisterSet.ZERO),
			PinstructionSet.buildInstruction(PinstructionSet.SUB, RegisterSet.BG, RegisterSet.BG, RegisterSet.ONE),
			PinstructionSet.buildInstruction(PinstructionSet.SYSCALL, RegisterSet.AG, RegisterSet.BG, RegisterSet.CG, RegisterSet.DG, RegisterSet.EG),
			PinstructionSet.buildInstruction(PinstructionSet.RET)
			
		};
		int instructionsm2[] = {
				/** main */			
				PinstructionSet.buildInstruction(PinstructionSet.MOVEIMMHIGH, RegisterSet.BV, getHigh(0)),			

				PinstructionSet.buildInstructionImm(PinstructionSet.CALL, getLow(3)), //split
				PinstructionSet.buildInstructionImm(PinstructionSet.CALL, getLow(8)), //transfer self (amount)			
				PinstructionSet.buildInstructionImm(PinstructionSet.CALL, getLow(17)), //activate		
				
				PinstructionSet.buildInstructionImm(PinstructionSet.JMPABS, getLow(0))

		};
		int pc = 0;
				
		while(pc < instructionsm1.length)
		{			
			newGuy.instruction.write(pc, instructionsm1[pc]);
			pc++;
		}
		
		pc = 100;
		while(pc < 100 + instructionsm2.length)
		{			
			newGuy.instruction.write(pc, instructionsm2[pc-100]);
			pc++;
		}
		
		return newGuy;
	} 
	
	
}
